package secloud

import (
	"context"
	"errors"
	"github.com/antihax/optional"
)

type KmsClient struct {
	Base *BaseClient
	ctx  context.Context
}

func NewKmsClient(ctx context.Context) *KmsClient {
	return &KmsClient{
		Base: BaseSvc,
		ctx:  ctx,
	}
}

type GenerateKeyOpts struct {
	WorkMode     optional.String
	Algorithm    optional.String
	Count        optional.Int
	KeyAlgorithm optional.String
	EpkName      optional.String
}

type QueryBgTaskOpts struct {
	BgTaskId optional.String
}

type InvokeKeyOpts struct {
	WorkMode optional.String
	Count    optional.Int
}

type RenewKeyOpts struct {
	KeyId        optional.String
	KeyAlgorithm optional.String
	EpkName      optional.String
}

type RevokeKeyOpts struct {
	KeyId   optional.String
	Message optional.String
}

type DestroyKeyOpts struct {
	KeyId optional.String
}

type OpenKeySessionOpts struct {
	WorkMode  optional.String
	Algorithm optional.String
	KeyId     optional.String
}

type CloseKeySessionOpts struct {
	SessionId optional.String
}

type ExportPublicKeyOpts struct {
	KeyUsage  optional.String
	Algorithm optional.String
}

func (kc *KmsClient) GenerateKey(opts *GenerateKeyOpts) (*GenerateKeyRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if opts.WorkMode.Value() == "EPK" && !opts.EpkName.IsSet() {
		return nil, errors.New("使用epk类型时必须指定epkName！")
	}
	if opts.WorkMode.Value() == "IPK" && !opts.KeyAlgorithm.IsSet() {
		return nil, errors.New("使用ipk类型时必须指定keyAlgorithm！")
	}

	var ret GenerateWorkingKeyResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"work_mode":     opts.WorkMode.Value(),
			"algorithm":     opts.Algorithm.Value(),
			"count":         opts.Count.Value(),
			"key_algorithm": opts.KeyAlgorithm.Value(),
			"epk_name":      opts.EpkName.Value(),
		}).
		SetResult(&ret).
		Post("/kms/generateKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) QueryBgTask(opts *QueryBgTaskOpts) (*QueryBgTaskRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.BgTaskId.IsSet() {
		return nil, errors.New("请输入正确的后台任务ID！")
	}

	var ret QueryBgTaskResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"bgTask_id": opts.BgTaskId.Value(),
		}).
		SetResult(&ret).
		Post("/kms/queryBgTask")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) InvokeKey(opts *InvokeKeyOpts) ([]InvokeKeyRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.WorkMode.IsSet() {
		return nil, errors.New("领用所属工作类型不得为空！")
	}
	if !opts.Count.IsSet() {
		return nil, errors.New("领用数量不得小于1！")
	}

	var ret InvokeKeyResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"work_mode": opts.WorkMode.Value(),
			"count":     opts.Count.Value(),
		}).
		SetResult(&ret).
		Post("/kms/invokeKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) RenewKey(opts *RenewKeyOpts) (*RenewKeyRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.KeyId.IsSet() {
		return nil, errors.New("keyId不能为空！")
	}

	var ret RenewKeyResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"key_id":        opts.KeyId.Value(),
			"key_algorithm": opts.KeyAlgorithm.Value(),
			"epk_name":      opts.EpkName.Value(),
		}).
		SetResult(&ret).
		Post("/kms/renewKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) RevokeKey(opts *RevokeKeyOpts) (*RevokeKeyRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.KeyId.IsSet() {
		return nil, errors.New("keyId不能为空！")
	}

	var ret RevokeKeyResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"key_id":  opts.KeyId.Value(),
			"message": opts.Message.Value(),
		}).
		SetResult(&ret).
		Post("/kms/revokeKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) DestroyKey(opts *DestroyKeyOpts) (*DestroyKeyRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.KeyId.IsSet() {
		return nil, errors.New("keyId不能为空！")
	}

	var ret DestroyKeyResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"key_id": opts.KeyId.Value(),
		}).
		SetResult(&ret).
		Post("/kms/destroyKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) BackupKey() (*BackupRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret BackupResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetResult(&ret).
		Post("/kms/backupKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) RecoverKey() (*RecoverRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret RecoverResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetResult(&ret).
		Post("/kms/recoverKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) OpenKeySession(opts *OpenKeySessionOpts) (*OpenKeySessionRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.WorkMode.IsSet() {
		return nil, errors.New("导入模式不能为空！")
	}

	var ret OpenKeySessionResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"work_mode": opts.WorkMode.Value(),
			"key_id":    opts.KeyId.Value(),
			"algorithm": opts.Algorithm.Value(),
		}).
		SetResult(&ret).
		Post("/kms/openKeySession")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) CloseKeySession(opts *CloseKeySessionOpts) (*CloseKeySessionRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.SessionId.IsSet() {
		return nil, errors.New("会话ID不能为空！")
	}

	var ret CloseKeySessionResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"session_id": opts.SessionId.Value(),
		}).
		SetResult(&ret).
		Post("/kms/closeKeySession")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (kc *KmsClient) ExportPublicKey(opts *ExportPublicKeyOpts) (*ExportPublicKeyRespData, error) {
	if kc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}
	if !opts.Algorithm.IsSet() {
		return nil, errors.New("导出算法不能为空！")
	}

	var ret ExportPublicKeyResp
	_, err := kc.Base.C.R().
		SetHeader("Authorization", "Bearer "+kc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"key_usage": opts.KeyUsage.Value(),
			"algorithm": opts.Algorithm.Value(),
		}).
		SetResult(&ret).
		Post("/kms/exportPublicKey")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}
